package org.ccay.audit.service.impl;

import java.io.Serializable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.inject.Inject;
import javax.inject.Named;

import org.ccay.async.AsyncMessage;
import org.ccay.async.IMessageSender;
import org.ccay.audit.AuditException;
import org.ccay.core.log.Audit;
import org.ccay.core.log.AuditContext;
import org.ccay.core.log.DifAudit;
import org.ccay.core.log.IAuditor;
import org.ccay.core.log.NullAuditor;

/**
 * 审计日志生成者。<BR>
 * 负责生成对应的审计日志，并发送到审计日志消息队列。
 * 
 * Created：2011-9-8
 * @author chaowangbang
 * @version:
 */
@Named
public class AuditLogProducer {

	/** 异步处理器 **/
	@Inject
	private IMessageSender messageSender;
	
	/** 差异日志审计器的实例，线程安全的 **/
	private IAuditor difLogAuditor = new DifLogAuditor();
	
	/** 系统默认的日志审计器实例，线程安全的 **/
	private IAuditor defaultAuditor = new DefaultAuditor();
	
	/**
	 * IAuditor实例的缓存
	 */
	private ConcurrentMap<Class<?>, IAuditor> auditorCache = new ConcurrentHashMap<Class<?>, IAuditor>();

	/**
	 * 执行审计操作
	 * 
	 * @param auditContext
	 */
	public void audit(AuditContext auditContext) {
		Audit audit = auditContext.getMethod().getAnnotation(Audit.class);
		DifAudit difAudit = auditContext.getMethod().getAnnotation(DifAudit.class);
		
		/** 获取合适的auditor实例 **/
		IAuditor auditor = null;
		
		/** auditor属性的优先级最高 **/
		Class auditorClass = audit.auditor();
		if(auditorClass!=null && auditorClass!=NullAuditor.class) { // 用户自定义了auditor
			auditor = buildAuditor(auditorClass);
		} else if(difAudit!=null){ //需要记录差异日志
			auditor = difLogAuditor;
		} else { // 记录普通的操作日志
			auditor = defaultAuditor;
		}
		
		Serializable logMessage = auditor.buildMessage(auditContext);

		if(logMessage != null) {
			AsyncMessage message = new AsyncMessage("AuditLog");
			message.setContent(logMessage);
			messageSender.send(message);
		}
	}

	/**
	 * 构建一个IAuditor实例，规则如下：<BR>
	 * 1. 用户明确指定了auditor属性，则依此为准；
	 * 2. 否则，用户是否指定了subject属性，如果指定了，则返回一个差异日志的IAuditor实现；
	 * 3. 否则使用DefaultAuditor 
	 * 
	 * @param audit
	 * @param auditContext
	 * @return
	 * @throws AuditException
	 */
	private IAuditor buildAuditor(Class<?> auditorClass)
			throws AuditException {
		if(auditorCache.containsKey(auditorClass)) {
			return auditorCache.get(auditorClass);
		} else {
			IAuditor auditor = null;
			try {
				auditor = (IAuditor) auditorClass.newInstance();
			} catch (Exception ex) {
				//异常处理
				throw new AuditException("ccay.log.00010001", ex);
			}
			
			auditorCache.put(auditorClass, auditor);
			
			return auditor;
		}
	}

}
